/*
 *  Box2DDebugRenderer.cpp
 *  BugFix
 *
 *  Created by Dmitry Fadeev on 9/14/11.
 *  Copyright 2011 Arello Mobile. All rights reserved.
 *
 */

#include <physics/Box2DDebugRenderer.h>
#include <lib3d/Math_lib3d.h>

namespace physics
{
	DebugVertices::DebugVertices(lib3d::hard::VertexBuffer * linesVB, lib3d::hard::VertexBuffer * trianglesVB) : linesVB(linesVB), trianglesVB(trianglesVB)
	{
		linesVB->set_primitives_type(lib3d::hard::LINESLIST);
	}
	
	DebugVertices::~DebugVertices()
	{
		delete linesVB;
		delete trianglesVB;
		
		linesVB     = NULL;
		trianglesVB = NULL;
	}
	
	Box2DDebugRenderer::Box2DDebugRenderer()
	{
		SetFlags(e_shapeBit | e_jointBit | e_aabbBit | e_centerOfMassBit);
		
		lib3d::hard::Manager::add_dependent(this);
	}
	
	Box2DDebugRenderer::~Box2DDebugRenderer()
	{
		lib3d::hard::Manager::remove_dependent(this);
	}
	
	void Box2DDebugRenderer::on_add_hardware(lib3d::hard::Manager::hardware_type * hardware)
	{
		hard_vector.push_back(SweetPair(hardware, new DebugVertices(hardware->create_vertex_buffer(), hardware->create_vertex_buffer())));
	}
	
	void Box2DDebugRenderer::Clear()
	{
		_linesList.clear();
		_trianglesList.clear();
	}
	
	void Box2DDebugRenderer::RenderToList(lib3d::hard::Hardware * hardware, lib3d::hard::RenderList * renderList)
	{
		// getting data for this hardware
		DebugVertices * object = find_data(hardware);
		if(object == NULL)
			return;
		
		// arrays for lines
		SimpleArray<float> linesPositions(_linesList.size() * 3);
		SimpleArray<float> linesColors(_linesList.size() * 4);
		
		// getting pointers to data
		float * positionsData = linesPositions.data();
		float * colorData     = linesColors.data();

		// fill arrays
		for(size_t i = 0; i < _linesList.size(); i++)
		{
			*positionsData++ = _linesList[i].position.x;
			*positionsData++ = _linesList[i].position.y;
			*positionsData++ = _linesList[i].position.z;
			
			*colorData++     = _linesList[i].color.r;
			*colorData++     = _linesList[i].color.g;
			*colorData++     = _linesList[i].color.b;
			*colorData++     = _linesList[i].alpha;
		}
		
		// empty float array
		SimpleArray<float> emptyFloat;
		
		// fill lines
		if(!object->linesVB->update(linesPositions, emptyFloat, linesColors, emptyFloat, emptyFloat))
			THROW(Gluck(__FUNCTION__, "linesVB->update() failed"));
		
		// fill render model
		lib3d::hard::RenderModel renderModel;
		renderModel.material.transparency = 0.0001f;
		renderModel.world           = lib3d::Matrix::identity();
		renderModel.vb              = object->linesVB;
		renderModel.primitive_start = 0;
		renderModel.primitive_count = _linesList.size() / 2;
		renderModel.ib              = NULL;
		renderModel.vertex_start    = 0;
		renderModel.vertex_count    = _linesList.size();
		
		// add to render list
		renderList->models.push_back(renderModel);
		
		// arrays for triangles
		SimpleArray<float> trianglesPositions(_trianglesList.size() * 3);
		SimpleArray<float> trianglesColors(_trianglesList.size() * 4);
		
		// getting pointers to data
		positionsData = trianglesPositions.data();
		colorData     = trianglesColors.data();
		
		// fill arrays
		for(size_t i = 0; i < _trianglesList.size(); i++)
		{
			*positionsData++ = _trianglesList[i].position.x;
			*positionsData++ = _trianglesList[i].position.y;
			*positionsData++ = _trianglesList[i].position.z;
			
			*colorData++     = _trianglesList[i].color.r;
			*colorData++     = _trianglesList[i].color.g;
			*colorData++     = _trianglesList[i].color.b;
			*colorData++     = _trianglesList[i].alpha;
		}
		
		// fill lines
		if(!object->trianglesVB->update(trianglesPositions, emptyFloat, trianglesColors, emptyFloat, emptyFloat))
			THROW(Gluck(__FUNCTION__, "trianglesVB->update() failed"));
		
		// fill render model
		lib3d::hard::RenderModel renderModel2;
		renderModel2.material.transparency = 0.0001f;
		renderModel2.world                 = lib3d::Matrix::identity();
		renderModel2.vb                    = object->trianglesVB;
		renderModel2.primitive_start       = 0;
		renderModel2.primitive_count       = _trianglesList.size() / 3;
		renderModel2.ib                    = NULL;
		renderModel2.vertex_start          = 0;
		renderModel2.vertex_count          = _trianglesList.size();
		
		// add to render list
		renderList->models.push_back(renderModel2);
	}
	
	void Box2DDebugRenderer::DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
	{
		for(int i = 0; i < vertexCount; i++)
		{
			_linesList.push_back(DebugVertex(vertices[i].x * 100.0f, vertices[i].y * 100.0f, color.r, color.g, color.b, 1.0f));
			
			if(i == vertexCount - 1)
			{
				_linesList.push_back(DebugVertex(vertices[0].x * 100.0f, vertices[0].y * 100.0f, color.r, color.g, color.b, 1.0f));
			}
			else
			{
				_linesList.push_back(DebugVertex(vertices[i + 1].x * 100.0f, vertices[i + 1].y * 100.0f, color.r, color.g, color.b, 1.0f));
			}
		}
	}
	
	void Box2DDebugRenderer::DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color)
	{
		b2Vec2 center = b2Vec2(0.0f, 0.0f);
		for(int i = 0; i < vertexCount; i++)
		{
			center.x += vertices[i].x;
			center.y += vertices[i].y;
		}
		
		center.x /= vertexCount;
		center.y /= vertexCount;
		
		for(int i = 0; i < vertexCount; i++)
		{
			if(i != vertexCount - 1)
			{
				_trianglesList.push_back(DebugVertex(vertices[i + 1].x * 100.0f, vertices[i + 1].y * 100.0f, color.r, color.g, color.b, 0.5f));
			}
			else
			{
				_trianglesList.push_back(DebugVertex(vertices[0].x * 100.0f, vertices[0].y * 100.0f, color.r, color.g, color.b, 0.5f));
			}
			_trianglesList.push_back(DebugVertex(vertices[i + 0].x * 100.0f, vertices[i + 0].y * 100.0f, color.r, color.g, color.b, 0.5f));
			_trianglesList.push_back(DebugVertex(center.x          * 100.0f, center.y          * 100.0f, color.r, color.g, color.b, 0.5f));
		}
	}
	
	void Box2DDebugRenderer::DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color)
	{
		for(int i = 0; i < 36; i++)
		{
			float sx = sinf(((i + 0) * 10.0f) / 180.0f * 3.1415f) * radius * 100.0f;
			float sy = cosf(((i + 0) * 10.0f) / 180.0f * 3.1415f) * radius * 100.0f;
			float dx = sinf(((i + 1) * 10.0f) / 180.0f * 3.1415f) * radius * 100.0f;
			float dy = cosf(((i + 1) * 10.0f) / 180.0f * 3.1415f) * radius * 100.0f;
			
			_linesList.push_back(DebugVertex(center.x * 100.0f + sx, center.y * 100.0f + sy, color.r, color.g, color.b, 1.0f));
			_linesList.push_back(DebugVertex(center.x * 100.0f + dx, center.y * 100.0f + dy, color.r, color.g, color.b, 1.0f));
		}
	}
	
	void Box2DDebugRenderer::DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color)
	{
		for(int i = 0; i < 36; i++)
		{
			float sx = sinf(((i + 0) * 10.0f) / 180.0f * 3.1415f) * radius * 100.0f;
			float sy = cosf(((i + 0) * 10.0f) / 180.0f * 3.1415f) * radius * 100.0f;
			float dx = sinf(((i + 1) * 10.0f) / 180.0f * 3.1415f) * radius * 100.0f;
			float dy = cosf(((i + 1) * 10.0f) / 180.0f * 3.1415f) * radius * 100.0f;
			
			_trianglesList.push_back(DebugVertex(center.x * 100.0f,      center.y * 100.0f,      color.r, color.g, color.b, 0.5f));
			_trianglesList.push_back(DebugVertex(center.x * 100.0f + sx, center.y * 100.0f + sy, color.r, color.g, color.b, 0.5f));
			_trianglesList.push_back(DebugVertex(center.x * 100.0f + dx, center.y * 100.0f + dy, color.r, color.g, color.b, 0.5f));
		}
	}
	
	void Box2DDebugRenderer::DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color)
	{
		_linesList.push_back(DebugVertex(p1.x * 100.0f, p1.y * 100.0f, color.r, color.g, color.b, 1.0f));
		_linesList.push_back(DebugVertex(p2.x * 100.0f, p2.y * 100.0f, color.r, color.g, color.b, 1.0f));
	}
	
	void Box2DDebugRenderer::DrawTransform(const b2Transform& xf)
	{
		_linesList.push_back(DebugVertex(xf.p.x * 100.0f, xf.p.y * 100.0f, 1.0f, 0.0f, 0.0f, 1.0f));
		_linesList.push_back(DebugVertex(xf.p.x * 100.0f + xf.q.GetXAxis().x * 20, xf.p.y * 100.0f + xf.q.GetXAxis().y * 20, 1.0f, 0.0f, 0.0f, 1.0f));
		
		_linesList.push_back(DebugVertex(xf.p.x * 100.0f, xf.p.y * 100.0f, 0.0f, 1.0f, 0.0f, 1.0f));
		_linesList.push_back(DebugVertex(xf.p.x * 100.0f + xf.q.GetYAxis().x * 20, xf.p.y * 100.0f + xf.q.GetYAxis().y * 20, 0.0f, 1.0f, 0.0f, 1.0f));
	}
}